﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Motoball.screenmanager
{
    public abstract class MenuScreen : GameScreen
    {
        protected bool toNextMenu = false;
        protected enum ScreenState
        {
            //is set when menu enters the screen
            Entering,
            //is set when menu exits the screen
            Exiting,
            //is set when menu is rotating
            Rotating,
            //is set when menu is ready(no more scaling or rotating)
            Ready
        }
        protected ScreenState screenState = ScreenState.Entering;
        protected List<MenuItem> items;
        //stores all items that are currently visible (except the screenleaving one)
        protected int[] visibleItems = new int[3];
        protected int leavingItem = -1;

        /// <summary>
        /// is set to >0 if only 3 items are in current menu and one item has
        /// to "beam" to other' side start as soon as it's invisible
        /// </summary>
        int beamItem = -1;
        
        
        static float menuItemDistance = MathHelper.PiOver4 * 1.25f;
        static float rightEntryPoint = MathHelper.Pi - menuItemDistance * 2;
        static float leftEntryPoint = MathHelper.Pi + menuItemDistance * 2;
        static float leftExitPoint = 4.8f;//MathHelper.Pi + MathHelper.PiOver2;
        static float rightExitPoint = 2.0f;
        //float scale = 1.0f;
        bool clockwise = true;
        //speed and direction items rotate
        static float rotationRadian = 3.0f;


        public MenuScreen(string screenName)
            :base(screenName)
        {   
        }

        #region Methods
        public override void Draw(GameTime gameTime)
        {
            for (int i = 0; i < 3; i++)
            {
                if (visibleItems[i] != -1)
                    items[visibleItems[i]].Draw(Camera, gameTime);
            }
            //memo:check if items visible?
            if (leavingItem != -1)
                items[leavingItem].Draw(Camera, gameTime);
        }

        public override void Initialize() 
        {
            for(int i=0; i<3; i++)
                visibleItems[i] = -1;
            items = new List<MenuItem>();
        }

        public MenuItem GetItem(int position)
        {
            try
            { return items.ElementAt(position); }
            catch(System.ArgumentOutOfRangeException)
            { return null; }
        }

        public void AddItem(MenuItem item)
        {
            items.Add(item);
            switch(items.Count)
            {
                case 0:
                    //should not happen
                    break;
                case 1:
                    visibleItems[1] = 0;
                    break;
                case 2:
                    visibleItems[1] = 1;
                    visibleItems[0] = 0;
                    break;
                default:
                    visibleItems[2] = 2;
                    visibleItems[1] = 1;
                    visibleItems[0] = 0;
                    break;
            }
        }

        public void AddItem(string itemName)
        {
            AddItem(new MenuItem(new BasicModel(screenManager.Content.Load<Model>(itemName))));

        }

        /// <summary>
        /// starts rotation of menu-entries;
        /// numberRotations equals how often a whole 360° rotation has to be drawn
        /// </summary>
        private void StartRotation()
        {
            screenState = ScreenState.Rotating;
            if(clockwise)
            {
                if(visibleItems[2] != -1 && visibleItems[2] != beamItem)
                    items[visibleItems[2]].Rotation = rightEntryPoint;
                if(rotationRadian < 0.0f)
                    rotationRadian = -rotationRadian;
            }
            else
            {
                if(visibleItems[0] != -1 && visibleItems[0] != beamItem)
                    items[visibleItems[0]].Rotation = leftEntryPoint;
                if(rotationRadian > 0.0f)
                    rotationRadian = -rotationRadian;
            }
            
        }

        /*
        /// <summary>
        /// checks for all items if menuSwitchSound 
        /// has to be played and if so it plays
        /// </summary>
        private void CheckSwitchSound()
        {
            //if switchSoundItem isn't set yet or has passes a certain area
            //we have a look for other items that may trigger the sound
            if (!CheckItemSound(switchSoundItem))
            {
                foreach (MenuItem item in items)
                {
                    if (CheckItemSound(item))
                    {
                        AudioManager.PlayCue("menu_switch");
                        switchSoundItem = item;
                        break;
                    }
                }
            }
        }
        */
        
        /*
        /// <summary>
        /// returns true if an item' rotation is smaller than Pi/4
        /// or greater than Pi*3/4
        /// </summary>
        /// <param name="item">item which rotation shall be looked at</param>
        /// <returns></returns>
        private bool CheckItemSound(MenuItem item)
        {
            if (item == null)
                return false;
            float itemRotation = MathHelper.PiOver4;
            if (clockwise)
            {
                if (item.Rotation > MathHelper.TwoPi-itemRotation)
                    return true;
                else
                    return false;
            }
            else
            {
                if (item.Rotation < itemRotation)
                    return true;
                else
                    return false;
            }
        }
        */

        /// <summary>
        /// called at start of counterclockwise rotation
        /// </summary>
        public void RotateCounterClockwise()
        {
            int count = items.Count - 1;
            if (count == 0)
            {
                AudioManager.PlayCue("no_energy");
                return;
            }
            clockwise = false;
            
            //sets item-marks appropriate to rotation-direction
            if (count >= 2)
            {
                leavingItem = visibleItems[2];
                if (--visibleItems[0] < 0)
                    visibleItems[0] = count;
                if (--visibleItems[1] < 0)
                    visibleItems[1] = count;
                if (--visibleItems[2] < 0)
                    visibleItems[2] = count;
                if (count == 2)
                {
                    beamItem = visibleItems[0];
                    leavingItem = -1;
                }   
            }
            //if no ringwise-rotation possible
            else
            {
                //rotate clockwise if limit reached
                if (visibleItems[1] == 0)
                {
                    RotateClockwise();
                    return;
                }
                else
                {
                    visibleItems[0] = -1;
                    visibleItems[1] = 0;
                    visibleItems[2] = 1;
                }
            }
            StartRotation();
        }
        
        /// <summary>
        /// called at start of clockwise rotation
        /// </summary>
        public void RotateClockwise()
        {
            int count = items.Count - 1;
            if (count == 0)
            {
                AudioManager.PlayCue("no_energy");
                return;
            }
            clockwise = true;
            
            //sets item-marks appropriate to rotation-direction
            if (count >= 2)
            {
                leavingItem = visibleItems[0];
                if (++visibleItems[0] > count)
                    visibleItems[0] = 0;
                if (++visibleItems[1] > count)
                    visibleItems[1] = 0;
                if (++visibleItems[2] > count)
                    visibleItems[2] = 0;
                if (count == 2)
                {
                    beamItem = visibleItems[2];
                    leavingItem = -1;
                }
            }
            //if no ringwise-rotation possible
            else
            {
                //rotate counter-clockwise if limit reached
                if (visibleItems[1] == 1)
                {
                    RotateCounterClockwise();
                    return;
                }
                else
                {
                    visibleItems[0] = 0;
                    visibleItems[1] = 1;
                    visibleItems[2] = -1;
                }
            }
            StartRotation();
        }
        
        /// <summary>
        /// rotates all menu items by rotation radian 
        /// (usually part of Update()-Call)
        /// </summary>
        /// <returns>returns true if rotation has finished, false otherwise</returns>
        private bool RotateAll(GameTime gameTime)
        {
            float rotation = rotationRadian * (float)gameTime.ElapsedGameTime.TotalSeconds;
            for(int i=0; i<3; i++)
            {
                if(visibleItems[i] != -1 && visibleItems[i] != beamItem)
                    items[visibleItems[i]].Update(gameTime, rotation);
            }
            if (beamItem != -1)
            {
                items[beamItem].Update(gameTime, rotation);
                if (clockwise && items[beamItem].Rotation >= leftExitPoint)
                {
                    items[beamItem].Rotation = items[visibleItems[1]].Rotation - menuItemDistance;
                    beamItem = -1;
                }
                else if(!clockwise && items[beamItem].Rotation <= rightExitPoint)
                {
                    items[beamItem].Rotation = items[visibleItems[1]].Rotation + menuItemDistance;
                    beamItem = -1;
                }   
            }
            if(leavingItem != -1)
                items[leavingItem].Update(gameTime, rotation);
            return items[visibleItems[1]].FinishedRotation;
        }

        public virtual void HandleInput()
        {
            switch(screenManager.Input.MenuButtonPressed)
            {
                    //nothing pressed
                case 0:
                    break;
                case 1:
                    //left
                    RotateCounterClockwise();
                    screenState = ScreenState.Rotating;
                    AudioManager.PlayCue("menu_switch_new");
                    break;
                case 2:
                    //right
                    RotateClockwise();
                    screenState = ScreenState.Rotating;
                    AudioManager.PlayCue("menu_switch_new");
                    break;
                case 3:
                    //enter
                    SelectEntry();
                    AudioManager.PlayCue("menu_click_new");
                    break;
                    //cancel
                case 4:
                    screenState = ScreenState.Exiting;
                    beamItem = -1;
                    toNextMenu = false;
                    AudioManager.PlayCue("menu_click_new");
                    break;
                default:
                    break;
            }

        }

        protected override void Exit()
        {
            if (toNextMenu)
            {
                screenManager.RemoveScreen(this);
                screenManager.ToNextMenu(TopItem);
            }
            else
                base.Exit();
        }
        /// <summary>
        /// called if enter pressed - add action in inheriting classes
        /// </summary>
        protected virtual void SelectEntry() 
        {
            toNextMenu = true;
            screenState = ScreenState.Exiting;  
        }

        public override void Update(GameTime gameTime)
        {
            switch(screenState)
            {
                case ScreenState.Entering:
                    Enter(gameTime);
                    break;

                case ScreenState.Exiting:
                    clockwise = true;
                    if (rotationRadian < 0.0f)
                        rotationRadian = -rotationRadian;
                    Exiting(gameTime);
                    break;

                case ScreenState.Rotating:
                    //update rotation-angle of all items
                    if (RotateAll(gameTime))
                    {
                        screenState = ScreenState.Ready;
                        CorrectRotation();
                    }
                    break;

                case ScreenState.Ready:
                    HandleInput();
                    break;

                default:
                    break;
            }        
        }

        protected virtual void Enter(GameTime gameTime)
        {
            RotateAll(gameTime);
            if (items[visibleItems[1]].FinishedRotation)
            {
                screenState = ScreenState.Ready;
                CorrectRotation();
                return;
            }
        }

        protected virtual void Exiting(GameTime gameTime)
        {
            RotateAll(gameTime);
            if (items[RightItem].Rotation >= leftEntryPoint)
            {
                Exit();
                return;
            }
        }
     
        public override void Reset()
        {
            toNextMenu = false;
            RotateToStart();
            clockwise = true;
            if (rotationRadian < 0.0f)
                rotationRadian = -rotationRadian;
            screenState = ScreenState.Entering;
        }

        /// <summary>
        /// rotate items to their entering-positions so they can enter the screen
        /// </summary>
        private void RotateToStart()
        {
            float rotation = rightEntryPoint;
            for (int i = 0; i < 3; i++)
            {
                if (visibleItems[i] != -1)
                {
                    items[visibleItems[i]].Rotation = rotation;
                }
                rotation -= menuItemDistance;
            }
        }

        
        /// <summary>
        /// sets visible items to their most exact position
        /// and set leaving item to -1 so it will no longer be updated nor drawn
        /// </summary>
        private void CorrectRotation()
        {
            float rot = MathHelper.Pi + menuItemDistance;
            for (int i = 0; i < 3; i++)
            {
                if (visibleItems[i] != -1)
                    items[visibleItems[i]].Rotation = rot;
                rot -= menuItemDistance;
            }
            leavingItem = -1;
        }

        
        #endregion

        #region Properties
        /// <summary>
        /// returns the very right item (visibleItem[2] or visibleItem[1] -
        /// depending on item-amount
        /// </summary>        
        public int RightItem
        {
            get
            {
                if (visibleItems[2] != -1)
                    return visibleItems[2];
                else
                    return visibleItems[1];
            }
        }
        /// <summary>
        /// sets topItem-mark to given value;
        /// sets previous- and following-item-mark to appropiate values
        /// or to -1 (if not item-amount is too less)
        /// </summary>
        public int TopItem
        {
            get { return visibleItems[1]; }
            set
            {
                int count = items.Count - 1;
                if (value < 0 || value > count)
                    return;
                else
                {
                    visibleItems[0] = visibleItems[2] = visibleItems[1] = value;
                    if (--visibleItems[0] < 0)
                    {
                        if (count <= 1)
                            visibleItems[0] = -1;
                        else
                            visibleItems[0] = count;
                    }
                    if (++visibleItems[2] > count)
                    {
                        if (count <= 1)
                            visibleItems[2] = -1;
                        else
                            visibleItems[2] = 0;
                    }
                }
                leavingItem = -1;
            }
        }
        #endregion
    }
}
